from world import *
from lib   import *

class Machine(object):
  "A machine holds many Acts."
  def __init__(self,str="") :
    """Constructor. Machines have a 'start' state 
    and a list of 'actions' and 'states'."""
    self.actions=[]
    self.states={}
    self.start= None
    self.create(str)

  def create(self) :
    "Build the actions within this machine (implemented by subclasses)"
    return self

  def state(self,name) :
    """
    Ensure a state called 'name' exists. If this the first state
    in this machine, make it the start state.
    """
    if self.states.has_key(name): 
      s = self.states[name]
    else:
      s = State(name)
      if not self.start: 
        self.start = s
      self.states[name] = s
    return s

  def act(self,before,after,tag,priority=1,guard=True,fx=True) :
    "Add an Act to this machine."
    t = Act(before,after,tag,priority,guard,fx)
    self.actions.append(t)
    return t
  
  def run(self,world=World(),lives=3) :
    "Find new Acts that are ready to go and fire them."
    global The
    The = world
    here = self.start
    while True:
      act = here.todo(lives)
      if act:
        here.onExit(act,world)
        act.sideEffect()
        here = act.there
        here.onEntry(act,world)
        if not here.goOn(act,world):
          break 
    return world

class Easy(Machine) :
  "Easy is a quick way of creating machines from a string."
  def create(self,str) :
    "Parsing the strings. Dirty."
    lines=filter(None,lstrips(str.split('\n')))
    last=None
    states = []
    for line in [lstrips(x.split('|')) for x in lines]: 
      if len(line) != 5:
        print 'bad line ' + line
        continue
      line[4],weight = self.priority(line[4])
      comment = line[3]
      here    = self.state(line[0] or last)
      there   = self.state(line[4])
      guard   = True
      if line[1]:
        guard = line[1].replace('?','The.')
        guard = eval('lambda : ' + guard)
      sideEffect = True
      if line[2]:
        sideEffect = line[2].replace('?','The.')
        sideEffect = eval('lambda : ' + sideEffect)
      if line[0] : last = line[0]
      self.act(here,there,comment,
               priority=weight, guard=guard,
               fx=sideEffect)
  def priority(self,str) :
    "If a line ends in * N, then the priority of that Act is N."
    if not '*' in str: return str,1.0
    tmp= str.split('*')
    return tmp[0],float(tmp[1])
